package se.energimyndigheten.lampguiden.lighting;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.GestureDetectorCompat;
import android.util.Log;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import org.json.JSONException;

import java.io.IOException;

import se.energimyndigheten.lampguiden.R;
import se.energimyndigheten.lampguiden.WebDialogFragment;
import se.energimyndigheten.lampguiden.XmlClickable;
import se.energimyndigheten.lampguiden.util.AssetsUtil;

public class RoomItemFragment extends Fragment implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, XmlClickable {

    public static final String EXTRA_ROOM_NAME = "room_name";

    private static final String IMAGE_PATH = "submodule_html/Basta_ljuset/Room_images/";
    private static final String TAG = RoomItemFragment.class.getSimpleName();
    private static final String BUNDLE_FILE = "drawable_file";
    private static final String BUNDLE_LIGHT_STATUS = "light_status";

    private GestureDetectorCompat mDetector;

    private ImageView mRoom1;
    private ImageView mRoom2;
    private ImageView currentView;
    private ImageView backView;
    private ImageView mInfoTab;
    private View mContainer;
    private String mRoomName;
    private Room mRoom;
    private boolean[] mLightStatus;
    private String mCurrentDrawableFile = "";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // don't care about instance state as long as RoomGalleryActivity uses FragmentPagerAdapter
        // and NOT FragmentStatePagerAdapter

        Bundle args = getArguments();
        mRoomName = args.getString(EXTRA_ROOM_NAME);
        try {
            mRoom = AssetsUtil.getRoomFromName(getActivity().getAssets(), mRoomName);
        } catch (JSONException jse) {
            jse.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        mLightStatus = new boolean[mRoom.getLights().length];
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.room_gallery_item, container, false);
        mContainer = (View) rootView.findViewById(R.id.roomView);
        mRoom1 = (ImageView) rootView.findViewById(R.id.room1);
        currentView = mRoom1;
        mRoom2 = (ImageView) rootView.findViewById(R.id.room2);
        backView = mRoom2;
        mInfoTab = (ImageView) rootView.findViewById(R.id.info_tab);
        mDetector = new GestureDetectorCompat(getActivity(), this);
        mDetector.setOnDoubleTapListener(this);
        mContainer.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mDetector.onTouchEvent(event);
                return true;
            }
        });
        return rootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        // post runnable to ensure we have measurement of views
        view.post(new Runnable() {
            @Override
            public void run() {
                loadRoom();
                loadHotSpots();
            }
        });
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString(EXTRA_ROOM_NAME, mRoom.getName());
        outState.putString(BUNDLE_FILE, mCurrentDrawableFile);
        outState.putBooleanArray(BUNDLE_LIGHT_STATUS, mLightStatus);
        super.onSaveInstanceState(outState);
    }

    private void loadRoom() {
        // Log.v(TAG, "loadRoom()");
        String imagePath = mCurrentDrawableFile;
        if (imagePath.equals("")) {
            imagePath = mRoom.getImageFormat(); // ->  Vardagsrum/vardagsrum_%d_%d_%d.jpg
            imagePath = imagePath.replaceAll("%d", "0");
        }
        try {
            currentView.bringToFront();
            currentView.setImageDrawable(AssetsUtil.getScaledDrawable(getActivity().getResources(), IMAGE_PATH + imagePath, mContainer.getMeasuredWidth(), mContainer.getMeasuredHeight()));
            currentView.setVisibility(View.VISIBLE);
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return;
        }
    }

    private void loadHotSpots() {
        // Log.v(TAG, "loadHotSpots");

        Room.Light[] hotSpots = mRoom.getLights();

        // calculate offset, align and show hot spots
        final Context ctx = this.getActivity();
        // get dimensions to align hotSpots properly
        final int cWidth = mContainer.getMeasuredWidth();
        final int cHeight = mContainer.getMeasuredHeight();

        Animation hotSpotAnimation = AnimationUtils.loadAnimation(ctx, R.anim.pulse);
        for (int i = 0, N = hotSpots.length; i < N; i++) {
            Room.Light light = hotSpots[i];
            int offsetLeft = (int) Math.floor(cWidth * light.getLeftOffset());
            int offsetTop = (int) Math.floor(cHeight * light.getTopOffset());
            int offsetRight = cWidth - offsetLeft;

            // get correct hotSpot
            ImageView[] image = new ImageView[2];
            if (i == 0) {
                image[0] = (ImageView) mContainer.findViewById(R.id.hotspot_1_1);
                image[1] = (ImageView) mContainer.findViewById(R.id.hotspot_1);
            } else if (i == 1) {
                image[0] = (ImageView) mContainer.findViewById(R.id.hotspot_2_1);
                image[1] = (ImageView) mContainer.findViewById(R.id.hotspot_2);
            } else {
                image[0] = (ImageView) mContainer.findViewById(R.id.hotspot_3_1);
                image[1] = (ImageView) mContainer.findViewById(R.id.hotspot_3);
            }

            // calculate layoutParams using offset from image center
            // align from right if last hot spot - to align the 'more info' icon to the left
            int alignLeft = Math.round(image[0].getDrawable().getMinimumWidth() / 2);
            int alignTop = Math.round(image[0].getDrawable().getMinimumHeight() / 2);
            int paddingLeft = i==2? 0 : offsetLeft - alignLeft;
            int paddingRight = i!=2? 0 : offsetRight - alignLeft;
            int paddingTop = offsetTop - alignTop;
            ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(image[0].getLayoutParams());
            params.setMargins(paddingLeft, paddingTop, paddingRight, 0);
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(params);
            if (paddingRight !=0) {
                layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            } else {
                layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
            }
            image[0].setLayoutParams(layoutParams);

            // and calculate offset for the other image
            alignLeft = Math.round(image[1].getDrawable().getMinimumWidth() / 2);
            alignTop = Math.round(image[1].getDrawable().getMinimumHeight() / 2);
            paddingLeft = offsetLeft - alignLeft;
            paddingTop = offsetTop - alignTop;
            paddingRight = i!=2? 0: offsetRight - alignLeft;
            hotSpots[i].setOffset(paddingLeft, paddingTop, paddingRight);
            paddingLeft = paddingRight != 0? 0 : paddingLeft;
            params = new ViewGroup.MarginLayoutParams(image[0].getLayoutParams());
            params.setMargins(paddingLeft, paddingTop, paddingRight, 0);
            layoutParams = new RelativeLayout.LayoutParams(params);
            if (paddingRight !=0) {
                layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            } else {
                layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
            }
            image[1].setLayoutParams(layoutParams);
        }
        // update room with lights containing offset information
        mRoom.setLights(hotSpots);
        // update visibility and animations
        updateHotSpots();
    }

    /**
     * Toggles visibility and animation of hot spots.
     */
    private void updateHotSpots() {
        // Log.v(TAG, "toggleHotSpotVisibility");
        final Context ctx = getActivity();
        ImageView[][] views = new ImageView[][]{
                new ImageView[]{(ImageView) mContainer.findViewById(R.id.hotspot_1), (ImageView) mContainer.findViewById(R.id.hotspot_1_1), (ImageView) mContainer.findViewById(R.id.hotspot_1_2)},
                new ImageView[]{(ImageView) mContainer.findViewById(R.id.hotspot_2), (ImageView) mContainer.findViewById(R.id.hotspot_2_1), (ImageView) mContainer.findViewById(R.id.hotspot_2_2)},
                new ImageView[]{(ImageView) mContainer.findViewById(R.id.hotspot_3), (ImageView) mContainer.findViewById(R.id.hotspot_3_1), (ImageView) mContainer.findViewById(R.id.hotspot_3_2)}};

        // looping animation when all dark
        Animation animPulse = AnimationUtils.loadAnimation(ctx, R.anim.pulse);

        // we have three or two hot spots - are they all false?
        boolean allDark = mLightStatus.length == 3 ? (!mLightStatus[0] && !mLightStatus[1] && !mLightStatus[2]) : (!mLightStatus[0] && !mLightStatus[1]);

        for (int i = 0, N = mLightStatus.length; i < N; i++) {
            if (mLightStatus[i]) {
                // light is lit - hide hot spots
                views[i][0].setVisibility(View.INVISIBLE);
                views[i][1].setVisibility(View.INVISIBLE);

                // show info icon
                ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(views[i][0].getLayoutParams());
                params.setMargins(params.leftMargin, params.topMargin + 40, 0, 0);
                RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(params);
                layoutParams.addRule(RelativeLayout.BELOW, views[i][1].getId());

                int rule = i==2? RelativeLayout.LEFT_OF : RelativeLayout.RIGHT_OF;
                layoutParams.addRule(rule, views[i][1].getId());

                views[i][2].setLayoutParams(layoutParams);
                views[i][2].setVisibility(View.VISIBLE);
                views[i][2].bringToFront();
            } else {
                // light is dark
                views[i][2].setVisibility(View.INVISIBLE);
                views[i][0].setVisibility(View.VISIBLE);
                views[i][0].bringToFront();
                if (allDark) {
                    // all lights are out - set proper animation
                    views[i][1].setVisibility(View.VISIBLE);
                    views[i][1].bringToFront();
                    views[i][0].clearAnimation();
                    views[i][0].setAnimation(animPulse);
                } else {
                    // this light is out, but not all
                    views[i][1].setVisibility(View.INVISIBLE);
                    views[i][0].clearAnimation();
                    views[i][0].setAnimation(animPulse);
                }
            }
        }
        // tab should always be top
        mInfoTab.bringToFront();
    }

    @Override
    public boolean onDown(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
                           float velocityX, float velocityY) {
        return true;
    }

    @Override
    public void onLongPress(MotionEvent event) {
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                            float distanceY) {
        return true;
    }

    @Override
    public void onShowPress(MotionEvent event) {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        Log.v(TAG, "onSingleTapUp");
        toggleLight(getIndexOfClosestHotSpot((int) event.getX(), (int) event.getY()));
        return false;
    }

    @Override
    public boolean onDoubleTap(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        return true;
    }
    /**
     * Return index of hot spot that is closest to position
     * @param posX x coordinate
     * @param posY y coordinate
     * @return index of closest hot spot
     */
    private int getIndexOfClosestHotSpot(int posX, int posY) {
        // find the closest hotSpot
        Room.Light[] hotSpots = mRoom.getLights();
        int closestXY = Integer.MAX_VALUE;
        int index = 0;
        for (int i = 0, N = hotSpots.length; i < N; i++) {
            int[] posXY = hotSpots[i].getOffset();
            int distanceXY = Math.abs(posX - posXY[0]) + Math.abs(posY - posXY[1]);
            if (distanceXY < closestXY) {
                index = i;
                closestXY = distanceXY;
            }
        }
        return index;
    }

    /**
     * Toggles a light on/off
     * @param index index of light
     */
    private void toggleLight(int index) {
        //Log.v(TAG, "toggleLight(" + index + ")");

        final Context ctx = getActivity();

        // invert light status
        mLightStatus[index] = !mLightStatus[index];

        // get filename of drawable
        String drawableFileName = mRoom.getImageFormat();
        for (int i = 0, N = mLightStatus.length; i < N; i++) {
            String flag = mLightStatus[i] ? "1" : "0";
            drawableFileName = drawableFileName.replaceFirst("%d", flag);
        }
        mCurrentDrawableFile = drawableFileName;

        // get the drawable from assets
        drawableFileName = IMAGE_PATH + drawableFileName;
        Drawable newRoom;
        try {
            newRoom = AssetsUtil.getScaledDrawable(ctx.getResources(), drawableFileName, mContainer.getMeasuredWidth(), mContainer.getMeasuredHeight());
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        // put new drawable to front and fade in if light was turned on
        backView.bringToFront();
        backView.setImageDrawable(newRoom);
        if (mLightStatus[index]) {
            // light was turned on
            Animation lightAnimation = AnimationUtils.loadAnimation(ctx, R.anim.appear);
            lightAnimation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    swapRoomViews();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            backView.setAnimation(lightAnimation);
        } else {
            // light was turned off
            backView.setVisibility(View.VISIBLE);
            swapRoomViews();
            backView.setVisibility(View.INVISIBLE);
        }

        // update the hot spots to reflect change
        updateHotSpots();
    }

    /**
     * Swap the pointers of currentView and backView
     */
    private void swapRoomViews() {
        // swap current and back view
        if (currentView.equals(mRoom1)) {
            currentView = mRoom2;
            backView = mRoom1;
        } else {
            currentView = mRoom1;
            backView = mRoom2;
        }
    }

    @Override
    public void onXmlClick(View view) {
        int index = -1;
        switch (view.getId()) {
            case R.id.hotspot_1_2:
                index = 0;
                break;
            case R.id.hotspot_2_2:
                index = 1;
                break;
            case R.id.hotspot_3_2:
                index = 2;
                break;
            case R.id.info_tab: {
                index = -1;
            }
        }
        String document = (index != -1 && mLightStatus[index]) ? mRoom.getLights()[index].getDocument() : mRoom.getDocument();
        WebDialogFragment dialogFragment = new WebDialogFragment();
        dialogFragment.show(getFragmentManager(), "dialog", document);
    }
}
